unit fSkeletalD;

interface

uses
  System.SysUtils,
  System.Classes,
  System.Types,
  System.Math,
  Vcl.Graphics,
  Vcl.Controls,
  Vcl.Forms,
  Vcl.Dialogs,
  Vcl.StdCtrls,
  Vcl.ComCtrls,
  Vcl.ExtCtrls,

  GLS.VectorFileObjects,
  GLS.Scene,
  GLS.Objects,
  GLS.Texture,
  GLS.Cadencer,
  GLS.SceneViewer,
  GLS.Graph,
  GLS.FileSMD,

  GLS.Material,
  GLS.Coordinates,
  GLS.BaseClasses,
  GLS.VectorGeometry,
  GLS.Utils;

type
  TFormSkeletal = class(TForm)
    GLScene1: TGLScene;
    GLSceneViewer1: TGLSceneViewer;
    GLCamera1: TGLCamera;
    GLLightSource1: TGLLightSource;
    Actor1: TGLActor;
    DummyCube1: TGLDummyCube;
    GLMaterialLibrary1: TGLMaterialLibrary;
    Timer1: TTimer;
    GLCadencer1: TGLCadencer;
    Panel1: TPanel;
    BULongJump: TButton;
    CheckBox1: TCheckBox;
    LabelFPS: TLabel;
    BUHighJump: TButton;
    XYZGrid1: TGLXYZGrid;
    RBWalk: TRadioButton;
    RBRun: TRadioButton;
    AnimationControler1: TGLAnimationControler;
    Panel2: TPanel;
    TrackBar1: TTrackBar;
    CBBlend: TCheckBox;
    procedure FormCreate(Sender: TObject);
    procedure GLSceneViewer1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure GLSceneViewer1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer);
    procedure Timer1Timer(Sender: TObject);
    procedure BULongJumpClick(Sender: TObject);
    procedure CheckBox1Click(Sender: TObject);
    procedure GLCadencer1Progress(Sender: TObject; const deltaTime, newTime: Double);
    procedure Actor1EndFrameReached(Sender: TObject);
    procedure BUHighJumpClick(Sender: TObject);
    procedure RBWalkClick(Sender: TObject);
    procedure RBRunClick(Sender: TObject);
    procedure TrackBar1Change(Sender: TObject);
    procedure CBBlendClick(Sender: TObject);
    procedure FormMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer;
      MousePos: TPoint; var Handled: Boolean);
  private
  public
    baseAnimation: String;
    mx, my: Integer;
  end;

var
  FormSkeletal: TFormSkeletal;

implementation

{$R *.DFM}

procedure TFormSkeletal.FormCreate(Sender: TObject);
begin
  var Path: TFileName := GetCurrentAssetPath();
  SetCurrentDir(Path + '\modelext');
  // We load the SMD model here
  // Note the actor was linked to a material library, and textures are loaded
  // automatically (4 textures are used by this model)
  //
  // Kind thanks to ~A.u.s.t.i.n. & Neal 'Guplik' Corbett for the model
  // and allowing its use ;)
  Actor1.LoadFromFile('trinityRage.smd');
  // Now we load the walk & run animations and "fix" their translation
  // (HL walk/run animations have a built-in "slide" that we don't want here)
  Actor1.AddDataFromFile('walk.smd');
  Actor1.Animations[1].MakeSkeletalTranslationStatic;
  Actor1.AddDataFromFile('run.smd');
  Actor1.Animations[2].MakeSkeletalTranslationStatic;
  // Then load the two jumps
  Actor1.AddDataFromFile('long_jump.smd');
  Actor1.AddDataFromFile('jump.smd');
  // And the 'look_left_right' blending animations, that we immediately
  // assign to the controler. The MakeSkeletalRotationDelta removes absolute
  // information from the SMD (which HL may use, but GLScene doesn't)
  Actor1.AddDataFromFile('look_left_right.smd');
  Actor1.Animations[5].MakeSkeletalRotationDelta;
  AnimationControler1.AnimationName := 'look_left_right';
  // Skeleton visible, and start with walk animation
  // (pseudo-animation 0 is for the static model in its default attitude)
  Actor1.OverlaySkeleton := True;
  baseAnimation := 'walk';
  Actor1.SwitchToAnimation(baseAnimation);
end;

procedure TFormSkeletal.FormMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer;
  MousePos: TPoint; var Handled: Boolean);
begin
  GLCamera1 := GLSceneViewer1.Camera;
  GLCamera1.AdjustDistanceToTarget(Power(1.1, WheelDelta / 120));
end;

procedure TFormSkeletal.RBWalkClick(Sender: TObject);
begin
  // user requested 'walk'
  baseAnimation := 'walk';
  Actor1.SwitchToAnimation(baseAnimation, True);
end;

procedure TFormSkeletal.RBRunClick(Sender: TObject);
begin
  // user requested 'run'
  baseAnimation := 'run';
  Actor1.SwitchToAnimation(baseAnimation, True);
end;

procedure TFormSkeletal.BULongJumpClick(Sender: TObject);
begin
  // Smoothly switch to Long Jump
  Actor1.SwitchToAnimation(3, True);
end;

procedure TFormSkeletal.BUHighJumpClick(Sender: TObject);
begin
  // Smoothly switch to High Jump
  Actor1.SwitchToAnimation(4, True);
end;

procedure TFormSkeletal.Actor1EndFrameReached(Sender: TObject);
begin
  // If we weren't walking, switch back to walk
  if Actor1.CurrentAnimation <> baseAnimation then
    Actor1.SwitchToAnimation(baseAnimation, True);
end;

procedure TFormSkeletal.CBBlendClick(Sender: TObject);
begin
  // Enable/disable blending by binding or unbinding the animation controler
  // to the actor
  if CBBlend.Checked then
  begin
    AnimationControler1.Actor := Actor1;
    TrackBar1Change(Self);
  end
  else
    AnimationControler1.Actor := nil;
end;

procedure TFormSkeletal.TrackBar1Change(Sender: TObject);
begin
  // Blending along the controler's animation is just a matter of adjusting
  // the ratio, with 0 = first frame and 1 = last frame.
  AnimationControler1.Ratio := TrackBar1.Position * 0.01;
end;

// Nothing fancy below, just the same old stuff

procedure TFormSkeletal.CheckBox1Click(Sender: TObject);
begin
  Actor1.OverlaySkeleton := CheckBox1.Checked;
end;

procedure TFormSkeletal.GLSceneViewer1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  mx := X;
  my := Y;
end;

procedure TFormSkeletal.GLSceneViewer1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
  if Shift <> [] then
  begin
    GLCamera1.MoveAroundTarget(my - Y, mx - X);
    mx := X;
    my := Y;
  end;
end;

procedure TFormSkeletal.Timer1Timer(Sender: TObject);
begin
  LabelFPS.Caption := Format('%.1f FPS', [GLSceneViewer1.FramesPerSecond]);
  GLSceneViewer1.ResetPerformanceMonitor;
end;

procedure TFormSkeletal.GLCadencer1Progress(Sender: TObject; const deltaTime, newTime: Double);
begin
  GLScene1.NotifyChange(nil);
end;

end.
